#include "../stdlib.hpp"
#include <purewell/code/url.hpp>
#include <purewell/code/hexa.hpp>

namespace purewell
{

PWUrl& gPWUrl(PWUrl::getInstance());

static const uint8_t g_url_table[] = {
	1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
	1,0,1,1,0,1,1,0, 0,0,0,1,0,0,0,1,
	0,0,0,0,0,0,0,0, 0,0,1,1,1,1,1,1,
	1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0, 0,0,0,1,1,1,1,0,
	1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0, 0,0,0,1,1,1,1,1,
	1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
};

PWUrl&
PWUrl::getInstance(void)
{
	static PWUrl inst;
	return inst;
}

PWUrl::PWUrl()
{
}

size_t
PWUrl::encode(char* obuf, const char* ibuf, size_t iblen) const
{
	const char* ib(ibuf);
	const char* ie(ibuf+iblen);
	size_t ret(0);
	char c;
	char o[2];

	while ( ib != ie )
	{
		c = *ib;
		if ( c == ' ')
		{
			*(obuf+ret) = '+';
			++ret;
		}
		else if ( g_url_table[(unsigned int)c] )
		{
			gPWHexa.encode(o, c, true);

			*(obuf+ret) = '%';
			*(obuf+ret+1) = o[0];
			*(obuf+ret+2) = o[1];
			ret += 3;
		}
		else
		{
			*(obuf+ret) = c;
			++ret;
		}

		++ib;
	}

	return ret;
}

std::ostream&
PWUrl::encode(std::ostream& os, std::istream& is) const
{
	char c;
	char o[2];

	while ( !is.eof() )
	{
		is.get(c);

		if ( !is.good() ) break;
		if ( g_url_table[(unsigned int)c] )
		{
			os << '%';
			gPWHexa.encode(o, c, true);
			os.write(o,2);
		}
		else
		{
			os << c;
		}
	}

	return os;
}

size_t
PWUrl::decode(char* obuf, const char* ibuf, size_t iblen) const
{
	const char* ib(ibuf);
	const char* ie(ibuf+iblen);
	char i[2];
	char c;
	bool raw(true);
	size_t ret(0);

	while ( ib != ie )
	{
		c = *ib;
		++ib;

		if ( raw )
		{
			if ( c == '%' )
			{
				raw = false;
				continue;
			}

			if ( c == '+' )
			{
				c = ' ';
			}

			*(obuf+ret) = c;

			++ret;
		}
		else
		{
			raw = true;

			i[0] = c;
			if ( ib != ie )
			{
				i[1] = *ib;
				++ib;
			}
			else
			{
				i[1] = 0x00;
			}

			*(obuf+ret) = gPWHexa.decode(i);
			++ret;
		}
	}

	return ret;
}

std::ostream&
PWUrl::decode(std::ostream& os, std::istream& is) const
{
	char i[2];
	char c;
	bool raw(true);

	while ( !is.eof() )
	{
		is.get(c);
		if ( !is.good() ) break;

		if ( raw )
		{
			if ( c == '%' )
			{
				raw = false;
				continue;
			}

			if ( c == '+' )
			{
				c = ' ';
			}

			os << c;
		}
		else
		{
			raw = true;

			i[0] = c;
			is.get(i[1]);
			if ( !is.good() )
			{
				i[1] = 0x00;
			}

			os << gPWHexa.decode(i);
		}
	}

	return os;
}

}; // purewell

